package org.folio.rest.impl.other.delayjob.partyattend;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import org.folio.cql2pgjson.exception.FieldException;
import org.folio.rest.impl.other.PartyUtil;
import org.folio.rest.impl.other.take.TakeInterface;
import org.folio.rest.impl.other.take.TenantsTaskService;
import org.folio.rest.jaxrs.model.Attend;
import org.folio.rest.jaxrs.model.Metadata;
import org.folio.rest.jaxrs.model.Party;
import org.folio.rest.jaxrs.model.ReaderReserveGroup;
import org.folio.rest.persist.PostgresClient;
import org.folio.rest.persist.cql.CQLWrapper;

import java.util.*;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.atomic.AtomicReference;


/**
 * @author lee
 * 控制活动到期之后,找出没签到的玩家,然后登记为未签到
 * 活动必须是已经审核之后的活动
 * 分每日扫描和管理员审核
 */
public class PartyAttendHandler implements TakeInterface,Runnable {
    private static volatile DelayQueue<PartyAttendDelayed> queueAttend = new DelayQueue<>();
    private final static Logger logger = LoggerFactory.getLogger("modparty");
    private static Vertx vertx;
    private static String tenant;
    @Override
    public void run() {
        while (true) {
            try {
                PartyAttendDelayed item = queueAttend.take();
                if (item != null) {
                    attendDateEndJob(item);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    public PartyAttendHandler(Vertx vertx, String tenant) {
        PartyAttendHandler.vertx = vertx;
        PartyAttendHandler.tenant= tenant;

        searchParty(tenant, vertx);
    }

    public   void initData() {
        searchParty(tenant, vertx);
   }



    private void searchParty(String tenant, Vertx vertx) {
        Date date = new Date();
        String dayStart = DateUtil.formatDateTime(DateUtil.beginOfDay(date));
        String dayEnd = DateUtil.formatDateTime(DateUtil.endOfDay(date));
        String query = "( isDel = 0 and attendEndDate >= " + dayStart + " and attendEndDate <= " + dayEnd + " and category <> 3 and state <> 1 and approval == 1 and isCalendar = 0  )";
        CQLWrapper cql = null;
        try {
            cql = PartyUtil.getCQL(query, -1, -1, "party");
        } catch (FieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        PostgresClient.getInstance(vertx, tenant).get("party", Party.class, new String[]{"*"}, cql,
                true, false, reply -> {
                    if (reply.succeeded()) {
                        List<Party> list = reply.result().getResults();
                        list.forEach(action -> {
                            setJob(action );
                        });
                    }
                });
    }


    private static PartyAttendDelayed createPartyDelay(Party partyGroup, String tenantId) {
        PartyAttendDelayed partyAttendDelayed = new PartyAttendDelayed();
        partyAttendDelayed.setExpTime(DateUtil.parse(partyGroup.getAttendEndDate()).getTime());
        partyAttendDelayed.setPartyId(partyGroup.getId());
        partyAttendDelayed.setTenantId(tenantId);
        return partyAttendDelayed;
    }
    @Override
    public void updatePartyAttendJob(Party partyGroup ) {
        Date date = new Date();
        if (DateUtil.isIn(DateUtil.parse(partyGroup.getAttendEndDate()),
                DateUtil.beginOfDay(date), DateUtil.endOfDay(date))) {
            AtomicReference<Boolean> flag = new AtomicReference<>(false);
            queueAttend.forEach(a -> {
                if (a.getTenantId().equals(tenant ) && a.getPartyId().equals(partyGroup.getId())) {
                    if (!DateUtil.formatDateTime(new Date(a.getExpTime())).equals(partyGroup.getAttendEndDate())) {
                        flag.set(true);
                    }
                    return;
                }
            });
            if (flag.get()) {
                //del job and add job
                delJob(partyGroup );
                createPartyDelay(partyGroup, tenant );
            }
        } else {
            AtomicReference<Boolean> flag = new AtomicReference<>(false);
            queueAttend.forEach(a -> {
                if (a.getTenantId().equals(tenant ) && a.getPartyId().equals(partyGroup.getId())) {
                    flag.set(true);
                    return;
                }
            });
            if (flag.get()) {
                //del job
                delJob(partyGroup );
            }
        }
    }

    @Override
    public void timeTake() {
       initData();
        logger.info("定时扫描今天签到结束的活动。。。");
    }

    @Override
    public   void delJob(Party partyGroup ) {
        if (ObjectUtil.isEmpty(partyGroup)) {
            logger.info(" add job err , partyGroup is null ");
            return;
        }

        if (StrUtil.isNullOrUndefined(tenant )) {
            logger.info(" add job err , tenantId is null ");
            return;
        }
        Date date = new Date();
        if (DateUtil.isIn(DateUtil.parse(partyGroup.getPartyEndDate()),
                DateUtil.beginOfDay(date), DateUtil.endOfDay(date))) {
            PartyAttendDelayed partyDelay = createPartyDelay(partyGroup, tenant );
            queueAttend.remove(partyDelay);
        }
    }
    @Override
    public void removeAndFreed(Party partyGroup ) {
        if (ObjectUtil.isEmpty(partyGroup)) {
            logger.info(" add job err , partyGroup is null ");
            return;
        }

        if (StrUtil.isNullOrUndefined(tenant )) {
            logger.info(" add job err , tenantId is null ");
            return;
        }
        Optional<PartyAttendDelayed> findResult = queueAttend.stream().filter(a -> a.getPartyId().equals(partyGroup.getId())).findAny();
        if (findResult.isPresent()) {
            queueAttend.remove(findResult.get());
        }

    }

    private void attendDateEndJob(PartyAttendDelayed partyDelay) {
        CQLWrapper cqlWrapper = null;
        try {
            String query = "(isDel = 0 and partyId == *" + partyDelay.getPartyId() + "*)";
            cqlWrapper = PartyUtil.getCQL(query, -1, -1, "r");
        } catch (Exception e) {
            logger.error("Incorrect request parameters");
            return;
        }
        PostgresClient pgc = PostgresClient.getInstance(this.vertx, partyDelay.getTenantId());
        String tenant = partyDelay.getTenantId();
        String sql = "SELECT %s FROM " + tenant + "_mod_party.reserve r LEFT JOIN " + tenant + "_mod_party.attend a ON r.jsonb ->> 'id' = a.jsonb ->> 'reserveId' ";
        String responseParams = "r.jsonb ->> 'id' AS reserve_id,r.jsonb ->> 'reserveDate' AS reserve_date, r.jsonb ->> 'partyName' AS party_name ," +
                "                 r.jsonb ->> 'partyStartDate' AS party_start_date , " +
                " r.jsonb ->> 'reserveDate' AS reserve_date , "
                + " r.jsonb -> 'readerReserveGroup' as reader_reserve_group ," +
                " a.jsonb ->> 'id' as attend_id ," +
                "  case when a.jsonb ->> 'attendState' is null then '5' else  a.jsonb ->> 'attendState' end attend_state ";

        HashMap<String, String> mapping = CollUtil.newHashMap();
        mapping.put("reserve_id", "reserveId");
        mapping.put("party_start_date", "partyStartDate");
        mapping.put("party_name", "partyName");
        mapping.put("attend_state", "attendState");
        mapping.put("attend_id", "attendId");
        mapping.put("reserve_date", "reserveDate");
        mapping.put("reader_reserve_group", "readerReserveGroup");


        String where = cqlWrapper != null ? cqlWrapper.toString() : "";

        String searchSQL = String.format(sql, responseParams) + where;
        logger.info(searchSQL);
        pgc.select(searchSQL, selectReply -> {
            if (selectReply.succeeded()) {

                List<JsonObject> list = selectReply.result().getRows();

                list.forEach(a -> {
                    Map<String, Object> map = a.getMap();
                    ReserveSearchResult readerReserveData = BeanUtil.mapToBean(map, ReserveSearchResult.class, CopyOptions.create().setFieldMapping(mapping));
                    JSONArray array = JSONUtil.parseArray(map.get("reader_reserve_group"));
                    List<ReaderReserveGroup> user = array.toList(ReaderReserveGroup.class);
                    readerReserveData.setReaderReserveGroup(user);
                    updateReaderReserve(readerReserveData, pgc, partyDelay.getPartyId() );
                });
            } else {
                logger.info("查找活动编号为【" + partyDelay.getPartyId() + "】的报名记录失败:{}", selectReply.cause());
            }

        });
    }

    private static void updateReaderReserve(ReserveSearchResult readerReserveData, PostgresClient pgc, String partyId ) {
        Attend attend = new Attend();
        attend.setAttendState(0);
        attend.setReserveDate(readerReserveData.getReserveDate());
        attend.setReserveId(readerReserveData.getReserveId());
        attend.setAttendDate(DateUtil.formatDateTime(new Date()));
        attend.setIsDel(0);
        attend.setPartyId(partyId);
        attend.setPartyName(readerReserveData.getPartyName());
        attend.setPartyStartDate(readerReserveData.getPartyStartDate());
        attend.setReaderReserveGroup(readerReserveData.getReaderReserveGroup());
        attend.setState(1);
        Metadata metadata = new Metadata();
        metadata.setCreatedDate(new Date());
        metadata.setUpdatedDate(new Date());
        metadata.setCreatedByUsername("sys");
        attend.setMetadata(metadata);
        if (readerReserveData.getAttendId() == null) {
            attend.setId(UUID.randomUUID().toString());
            pgc.save("attend", attend, reply -> {
                if (reply.succeeded()) {
                    TenantsTaskService.setJob(tenant,"blacklistService",attend);
                    logger.info("读者报名编号：【" + readerReserveData.getReserveId() + "】缺席登记处理成功 登记方式save");
                } else {
                    logger.info("读者报名编号【" + readerReserveData.getReserveId() + "】缺席登记处理失败: ", reply.cause());
                }
            });
        } else if (readerReserveData.getAttendId() != null && readerReserveData.getAttendState().equals("5")) {
            attend.setId(readerReserveData.getAttendId());
            pgc.update("attend", attend, readerReserveData.getAttendId(), reply -> {
                if (reply.succeeded()) {
                    if (reply.result().getUpdated() == 0) {
                        logger.info("读者报名编号【" + readerReserveData.getReserveId() + "】缺席登记处理失败");
                        return;
                    }
                    //黑名单
                    TenantsTaskService.setJob(tenant,"blacklistService",attend);
                    //BlacklistService.recordBehaviorAndCheck(attend);
                    logger.info("读者报名编号：【" + readerReserveData.getReserveId() + "】缺席登记处理成功 登记方式update");
                } else {
                    logger.info("读者报名编号【" + readerReserveData.getReserveId() + "】缺席登记处理失败:{}", reply.cause());
                }
            });
        }
     }

    @Override
    public void update() {
        initData();
        logger.info("定时扫描今天签到结束的活动。。。");
    }

    @Override
    public void setJob(Party partyGroup ) {
        if (StrUtil.isNullOrUndefined(partyGroup.getAttendEndDate())) {
            logger.info(" add job err , partyEndDate is null ");
            return;
        }
        Date date = new Date();
        if (DateUtil.isIn(DateUtil.parse(partyGroup.getAttendEndDate()),
                DateUtil.beginOfDay(date), DateUtil.endOfDay(date)) && "1".equals(partyGroup.getApproval().toString())) {
            PartyAttendDelayed partyDelay = createPartyDelay(partyGroup, tenant );
            queueAttend.add(partyDelay);
            logger.info("add queue job success, party name is " + partyGroup.getPartyName());
        } else {
            logger.info(" add job err , partyAttendDate is not today end ");
            return;
        }

    }
}
